package com.wen.controller;

import com.alibaba.excel.EasyExcel;
import com.alibaba.excel.ExcelWriter;
import com.alibaba.excel.write.metadata.WriteSheet;
import com.wen.entity.dto.AppUserDto;
import com.wen.entity.po.AppUser;
import com.wen.entity.query.AppUserQuery;
import com.wen.entity.vo.PaginationResultVO;
import com.wen.service.AppUserService;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.BeanUtils;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;

import javax.annotation.Resource;
import java.util.ArrayList;
import java.util.List;
import java.util.Map;
import java.util.concurrent.*;
import java.util.stream.Collectors;

@Slf4j
@RestController("multiExportController")
@RequestMapping("/export")
public class MultiExportController {
    private final ExecutorService executorService = new ThreadPoolExecutor(
            Runtime.getRuntime().availableProcessors(),
            Runtime.getRuntime().availableProcessors()*2,
            5,
            TimeUnit.SECONDS,
            new LinkedBlockingQueue<>(100),
            new ThreadPoolExecutor.CallerRunsPolicy()
    );

    String fileName = "D:\\POI\\export\\wen102.xlsx";
    String mulThreadExportFileName = "D:\\POI\\export\\wen101.xlsx";
    @Resource
    private AppUserService appUserService;
    @GetMapping("/page-export")
    public String export() {
        long start = System.currentTimeMillis();

        Integer total = appUserService.getTotalCount();
        log.info("total:{}",total);
        Integer pageSize = 5000;
        Integer totalPage = (total + pageSize - 1) / pageSize;;
        AppUserQuery query = new AppUserQuery();
        query.setPageSize(pageSize);
//        ExcelWriter excelWriter = EasyExcel.write(mulThreadExportFileName, AppUserDto.class).build();
        try(ExcelWriter excelWriter = EasyExcel.write(mulThreadExportFileName, AppUserDto.class).build()){
            for (int i = 1; i <= totalPage; i++){
                int finalI = i;
                query.setPageNo(i);
                List<AppUser> list = appUserService.getListByPage(query);
                List<AppUserDto> data = new ArrayList<>();
                for (AppUser appUser :list) {
                    AppUserDto appUserDto = new AppUserDto();
                    BeanUtils.copyProperties(appUser,appUserDto);
                    data.add(appUserDto);
                }
                WriteSheet writeSheet = EasyExcel.writerSheet(finalI,"模板"+finalI).build();
                excelWriter.write(data, writeSheet);
                log.info("当前线程完成第{}页导出,导出当前页数据{}条",i,list.size());
            }

        }
        long end = System.currentTimeMillis();
        log.info("耗时:{}秒",(end-start)/1000);
        return "多线程导出成功！";
    }


    @GetMapping("/filter-export")
    public String IdFilterexport() {
        long start = System.currentTimeMillis();

        Integer total = appUserService.getTotalCount();
        log.info("total:{}",total);
        Integer pageSize = 5000;
        Integer totalPage = (total + pageSize - 1) / pageSize;;
        AppUserQuery appUserQuery = new AppUserQuery();
        appUserQuery.setPageSize(pageSize);
        ExcelWriter excelWriter = EasyExcel.write(fileName, AppUserDto.class).build();
        WriteSheet writeSheet = EasyExcel.writerSheet("模板").build();
        int exportedCount = 0;
        long maxId = 0;
        for (int i = 1; i <= totalPage; i++){
            List<AppUserDto> list = new ArrayList<>();
            appUserQuery.setPageNo(i);
            PaginationResultVO<AppUser> listByPage = appUserService.findListByPage(appUserQuery);
            for (AppUser user:listByPage.getList()) {
                AppUserDto appUserDto = new AppUserDto();
                BeanUtils.copyProperties(user,appUserDto);
                maxId = Math.max(maxId,appUserDto.getId());
                list.add(appUserDto);
            }
            excelWriter.write( list,writeSheet);
            exportedCount += list.size();
            log.info("已导出第{}页，本页数据量:{}, 累计导出:{}", i, list.size(), exportedCount);
        }
        excelWriter.finish();
        long end = System.currentTimeMillis();
        log.info("耗时:{}秒",(end-start)/1000);
        return "多线程导出成功！";
    }
    @GetMapping("/mulquery-export")
    public String mulQueryOneThreadExport() throws InterruptedException {
        long start = System.currentTimeMillis();
        Integer total = appUserService.getTotalCount();
        Integer pageSize = 5000;
        Integer totalPage = (total + pageSize - 1) / pageSize;;
        log.info("total:{}",total);
        CountDownLatch countDownLatch = new CountDownLatch(totalPage);
        Map<Integer, List<AppUserDto>> map= new ConcurrentHashMap<>();
        for (int i = 1; i <= totalPage; i++){
            int finalI = i;
            executorService.execute(()->{
                log.info("当前线程：{}", Thread.currentThread().getName());
                AppUserQuery query = new AppUserQuery();
                query.setPageNo(finalI);
                query.setPageSize(pageSize);
                List<AppUser> records = appUserService.getListByPage(query);
                ArrayList<AppUserDto> list = new ArrayList<>();
                Integer curSum = 0;
                for (AppUser record : records) {
                    AppUserDto appUserDto = new AppUserDto();
                    BeanUtils.copyProperties(record,appUserDto);
                    list.add(appUserDto);
                    curSum+=list.size();
                }
                map.put(finalI,list);
                countDownLatch.countDown();
                log.info("当前线程{}完成查询,总共查询{}",
                        Thread.currentThread().getName(),list.size());
            });
        }
        countDownLatch.await();
        long queryEnd = System.currentTimeMillis();
        List<AppUserDto> mergeList = mergeByLoop(map);//汇总
        log.info("多线程查询到汇总耗时:{}秒,查询总数据{}条",(queryEnd-start)/1000,mergeList.size());
        executorService.execute(()->{
            long startExport = System.currentTimeMillis();
            log.info("当前线程{}开始写入文件", Thread.currentThread().getName());
            EasyExcel.write(fileName, AppUserDto.class).sheet("模板").doWrite(mergeList);
            log.info("当前线程{}写入文件完成,花费时间{}秒",
                    Thread.currentThread().getName(),
                    (System.currentTimeMillis()- startExport)/1000);
        });
        long end = System.currentTimeMillis();
        log.info("主线程{}完成导出接口，耗时:{}秒", Thread.currentThread().getName(),(end-start));
        return "多线程导出成功";
    }


    @GetMapping("/mul-export")
    public String mulThreadExport() throws InterruptedException {
        long start = System.currentTimeMillis();
        Integer total = appUserService.getTotalCount();
        Integer pageSize = 5000;
        Integer totalPage = (total + pageSize - 1) / pageSize;;
        log.info("total:{}",total);
        Map<Integer, List<AppUserDto>> map= new ConcurrentHashMap<>();
//        ExcelWriter excelWriter = EasyExcel.write(mulThreadExportFileName, AppUserDto.class).build();
        try(ExcelWriter excelWriter = EasyExcel.write(mulThreadExportFileName, AppUserDto.class).build()){
            for (int i = 1; i <= totalPage; i++){
                final int finalI = i;
                executorService.execute(()->{
                    log.info("当前线程：{}", Thread.currentThread().getName());
                    AppUserQuery query = new AppUserQuery();
                    query.setPageNo(finalI);
                    query.setPageSize(pageSize);
                    List<AppUser> records = appUserService.getListByPage(query);
                    ArrayList<AppUserDto> data = new ArrayList<>();
                    Integer curSum = 0;
                    if (!records.isEmpty()){
                        for (AppUser record : records) {
                            AppUserDto appUserDto = new AppUserDto();
                            BeanUtils.copyProperties(record,appUserDto);
                            data.add(appUserDto);
                            curSum+=data.size();
                        }
                    }
                    if (data!=null && !data.isEmpty()){
                        WriteSheet writeSheet = EasyExcel.writerSheet(finalI, "模板" + finalI).build();
                        excelWriter.write(data, writeSheet);
                        excelWriter.finish();
                    }

                    log.info("当前线程{}完成导出,总共导出{}条",
                            Thread.currentThread().getName(),data.size());
                });
            }
        }

        long end = System.currentTimeMillis();
        log.info("主线程{}完成导出接口，耗时:{}秒", Thread.currentThread().getName(),(end-start));
        return "多线程导出成功";
    }

    /**
     * 传统遍历方式合并Map中的多个List
     * @param map 存储List的Map，value为List<T>
     * @param <T> List中元素的类型
     * @return 合并后的总List
     */
    public static <T> List<T> mergeByLoop(Map<?, List<T>> map) {
        // 初始化一个足够容量的List，减少扩容开销
        List<T> mergedList = new ArrayList<>();
        if (map == null || map.isEmpty()) {
            return mergedList;
        }

        // 遍历Map中所有的List
        for (List<T> list : map.values()) {
            // 跳过null的List，避免空指针异常
            if (list != null) {
                mergedList.addAll(list);
            }
        }
        return mergedList;
    }
    /**
     * Stream API方式合并Map中的多个List
     * @param map 存储List的Map，value为List<T>
     * @param <T> List中元素的类型
     * @return 合并后的总List
     */
    public static <T> List<T> mergeByStream(Map<?, List<T>> map) {
        if (map == null || map.isEmpty()) {
            return new ArrayList<>();
        }

        // 过滤掉null的List，再将所有List扁平化为单个流，最后收集为List
        return map.values().stream()
                .filter(list -> list != null) // 排除null的List
                .flatMap(List::stream) // 将每个List转换为流并合并
                .collect(Collectors.toList());
    }
}
